#include "Fronteira.h"


FronteiraRigida::FronteiraRigida(MODEL*md)
{
    pModel=md;
    BoxMinH=pModel->BoxMin[0];
    BoxMinV=pModel->BoxMin[1];
    BoxMaxH=pModel->BoxMax[0];
    BoxMaxV=pModel->BoxMax[1];
}
FronteiraRigidaParalelo::FronteiraRigidaParalelo(MODEL*md)

            {
                pModel=md;
                BoxMinH=pModel->BoxMin[0];
                BoxMinV=pModel->BoxMin[1];
                BoxMaxH=pModel->BoxMax[0];
                BoxMaxV=pModel->BoxMax[1];
            }

FronteiraDestrutivel::FronteiraDestrutivel(MODEL*md)
{
    pModel=md;
    BoxMinH=pModel->BoxMin[0];
    BoxMinV=pModel->BoxMin[1];
    BoxMaxH=pModel->BoxMax[0];
    BoxMaxV=pModel->BoxMax[1];
}
FronteiraDestrutivelParalelo::FronteiraDestrutivelParalelo(MODEL*md)

{
    pModel=md;
    BoxMinH=pModel->BoxMin[0];
    BoxMinV=pModel->BoxMin[1];
    BoxMaxH=pModel->BoxMax[0];
    BoxMaxV=pModel->BoxMax[1];
    //listaDesbalanceada=false;
    qtdEsf=0;
    for (SPHERE* k = pModel->threads[0]; k != NULL; k = k->Next) qtdEsf++;
}

void FronteiraRigida::calcular()
{
    #pragma omp single
    {
        for (SPHERE* s = pModel->SPHList.Begin; s != NULL;s = s->Next)
        {
            if (s->R[0] < BoxMinH + s->Rad && s->V[0] < 0.0) { s->R[0] = BoxMinH + s->Rad; s->V[0] = -s->V[0]; }
            if (s->R[1] < BoxMinV + s->Rad && s->V[1] < 0.0) { s->R[1] = BoxMinV + s->Rad; s->V[1] = -s->V[1]; }
            if (s->R[0] > BoxMaxH - s->Rad && s->V[0] > 0.0) { s->R[0] = BoxMaxH - s->Rad; s->V[0] = -s->V[0]; }
            if (s->R[1] > BoxMaxV- s->Rad && s->V[1] > 0.0) { s->R[1] = BoxMaxV - s->Rad; s->V[1] = -s->V[1]; }
        }
    }
    #pragma omp barrier
}

void FronteiraDestrutivel::calcular()
{
    #pragma omp single
    {
        for (SPHERE* s = pModel->SPHList.Begin; s != NULL;s = s->Next)
        {
            if (s->R[0] < BoxMinH + s->Rad
            || s->R[1] < BoxMinV + s->Rad
            || s->R[0] > BoxMaxH - s->Rad
            || s->R[1] > BoxMaxV - s->Rad)
            {
                s = pModel->Delete(s);
                continue;
            }
        }
    }
    #pragma omp barrier
}

void FronteiraRigidaParalelo::calcular()
{
    #pragma omp for schedule (dynamic,1)
    for (int i = 0; i < pModel->numProceEsf; i++)
    {
        SPHERE* testeFim;
        if (i!=pModel->numProceEsf-1) testeFim = pModel->threads[i+1];
        else testeFim=NULL;
        for (SPHERE* s = pModel->threads[i]; s != testeFim; s = s->Next)
        {

            REAL R0;
            REAL R1;
            R0 = s->R[0];
            R1 = s->R[1];
            REAL V0;
            REAL V1;
            V0 = s->V[0];
            V1 = s->V[1];
            REAL Rad;
            Rad = s->Rad;

            if (R0 < BoxMinH + Rad && V0 < 0.0)
            {
                R0 = BoxMinH + Rad;
                V0 = -V0;
            }
            else if (R0 > BoxMaxH - Rad && V0 > 0.0)
            {
                R0 = BoxMaxH - Rad;
                V0 = -V0;
            }
            if (R1 < BoxMinV + Rad && V1 < 0.0)
            {
                R1 = BoxMinV + Rad;
                V1 = -V1;
            }
            else if (R1 > BoxMaxV - Rad && V1 > 0.0)
            {
                R1 = BoxMaxV - Rad;
                V1 = -V1;
            }
            s->R[0] = R0;
            s->R[1] = R1;
            s->V[0] = V0;
            s->V[1] = V1;
        }
    }
    //Barreira implicita
}

void FronteiraDestrutivelParalelo::calcular()
{
    #pragma omp for schedule (dynamic,1)
    for (int i = 0; i < pModel->numProceEsf; i++)
    {
        SPHERE* testeFim;
        if (i!=pModel->numProceEsf-1) testeFim = pModel->threads[i+1];
        else testeFim=NULL;
        for (SPHERE* s = pModel->threads[i]; s != testeFim; s = s->Next)
        {

            REAL Rad;
            Rad = s->Rad;

            if (s->R[0] < BoxMinH + Rad
            || s->R[1] < BoxMinV + Rad
            || s->R[0] > BoxMaxH - Rad
            || s->R[1] > BoxMaxV - Rad)
            {
                //Marco a esfera para destruir sequenciamente mais tarde
               //listaDesbalanceada=true;
                //s->destruir=true;
                #pragma omp critical
                {
                    remSequenc.push_back(s);
                }
                continue;
            }
        }
    }
    //barreira implícita
    #pragma omp single
    {
        //RemoverMarcados();
        RemoverListaMarcados();
    }
    #pragma omp barrier
}

void FronteiraDestrutivelParalelo::RemoverListaMarcados()
{
    if (!remSequenc.empty())
    {
        //Redistribua a lista entre os processadores
        int tamanho=0;
        int i=0;
        int offset;

        #ifdef TESTE
        assert( pModel->SPHList.Begin != NULL );
        assert( !remSequenc.empty() );
        #endif

        qtdEsf -=remSequenc.size();
        for (list<SPHERE*>::iterator it=remSequenc.begin() ; it != remSequenc.end(); it++ )
        {
            pModel->Delete(*it);
        }
        if (qtdEsf < 100) pModel->numProceEsf=1;
        offset=qtdEsf/pModel->numProceEsf;
        pModel->threads[0]=pModel->SPHList.Begin;
        for (SPHERE* k = pModel->threads[0]; k != NULL; k = k->Next)
        {
            tamanho++;
            if (tamanho == offset)
            {
                i++;
                pModel->threads[i] = k;
            }
        }
        remSequenc.clear();

        #ifdef TESTE
        assert( remSequenc.empty() );
        int numEsferas=0;
        for (SPHERE* k = pModel->threads[0]; k != NULL; k = k->Next) numEsferas++;
        assert(numEsferas==qtdEsf);
        #endif
    }
}
/*
void FronteiraDestrutivelParalelo::CorrigirPonteiros(int tamanho)
{
    int i=0;
    int offset;
    if (tamanho < 2000) pModel->numProceEsf=1;
    offset=tamanho/pModel->numProceEsf;
    for (SPHERE* k = pModel->threads[0]; k != NULL; k = k->Next)
    {
        tamanho++;
        if (tamanho == offset)
        {
            i++;
            pModel->threads[i] = k;
        }
    }
}
void FronteiraDestrutivelParalelo::RemoverMarcados()
{
    //Se esferas foram destruídas a lista não estará distribuída igualmente
    //Além disto temos que remover as esferas marcadas
    if (listaDesbalanceada==true)
    {
        //Redistribua a lista entre os processadores
        int tamanho=0;
        pModel->threads[0]=pModel->SPHList.Begin;
        for (SPHERE* k = pModel->SPHList.Begin; k != NULL;)
        {
            if (k->destruir)
            {
                k = pModel->Delete(k);
            }
            else
            {
                k = k->Next;
                tamanho++;
            }
        }
        CorrigirPonteiros(tamanho);
        listaDesbalanceada=false;
        //printf("Aviso: Algumas esferas foram removidas. Modelo com %d esferas.\n",tamanho);
    }
}
*/
